#pragma once

// 既然是封装套接字，我们就先把要用到的头文件先写上去
#include <iostream>
#include <string>
#include <sys/socket.h>
#include <unistd.h>
#include <sys/types.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include "log.hpp"
#include "Common.hpp"
#include "InetAddr.hpp"
#include <memory>
#include<functional>
#include <sys/wait.h>


using namespace LogModule;
class Socket;
using SockPtr = std::shared_ptr<Socket>;

// 所谓模板方法模式，本质上就是C++多态继承特性的应用，所以我们就需要先定义一个基类
// 用来规定创建Socket的方法
class Socket
{
public:
    virtual ~Socket() = default; // 防止不调用子类析构函数的情况出现

    virtual void SocketOrDie() = 0; // 创建套接字接口，这里的OrDie是一种常见的命名约定，特别是在C/C++和系统编程中。
    // 表示操作成功则继续，失败则直接终止程序,=0表示父类并没有实现，要求子类继承后必须实现
    //= default 表示显式要求编译器生成该函数的默认实现。

    virtual bool BindOrDie(int port) = 0;

    virtual void SetSocketOpt() = 0; // 这个我们之前没有接触到这个需求

    virtual bool ListenOrDie() = 0;

    virtual SockPtr Accepter(InetAddr *client) = 0;

    virtual void Close() = 0;

    virtual int Recv(std::string *out) = 0;

    virtual int Send(std::string &in) = 0;

    virtual int Fd()=0;

    // 模板方法：定义算法骨架
    void BuildTcpSocketMethod(int port)
    {
        SocketOrDie();   // 步骤1：创建socket
        SetSocketOpt();  // 步骤2：设置socket选项
        BindOrDie(port); // 步骤3：绑定端口
        ListenOrDie();   // 步骤4：开始监听
    }

private:
};

class TcpSocket : public Socket
{
public:
    TcpSocket(int fd) : _sockfd(fd)
    {
    }
    TcpSocket() : _sockfd(gdefaultsockfd)
    {
    }
    virtual ~TcpSocket()
    {
    }

    virtual void SocketOrDie() override
    {
        _sockfd = ::socket(AF_INET, SOCK_STREAM, 0);
        if (_sockfd < 0)
        {
            LOG(LogLevel::ERROR) << "socket create error";
            exit(SOCKET_ERR);
        }

        LOG(LogLevel::DEBUG) << "socket create success";
    }

    virtual bool BindOrDie(int port) override
    {
        if (_sockfd == gdefaultsockfd) // 说明没有进行获取套接字
        {
            return false;
        }

        InetAddr addr(port);
        int n = ::bind(_sockfd, addr.Getsockaddr(), addr.GetSockaddrLen());
        if (n < 0)
        {
            LOG(LogLevel::ERROR) << "bind error";
            exit(SOCKET_ERR);
        }
        LOG(LogLevel::DEBUG) << "bind create success: " << _sockfd;
        return true;
    }
    virtual void SetSocketOpt() override
    {
        //当我们的服务端主动断开连接或者异常断开连接时，再通过这个端口执行服务器，会产生绑定失败的问题
        //所以我们需要对套接字的属性进行设置
        int opt=1;
        int n=::setsockopt(_sockfd,SOL_SOCKET,SO_REUSEADDR,&opt,sizeof(opt));
    }
    virtual bool ListenOrDie() override
    {
        if (_sockfd == gdefaultsockfd)
        {
            return false;
        }
        int n = ::listen(_sockfd, gbacklog); // 我们这里之前应该提到过，第二个参数表示监听队列的长度
        if (n < 0)
        {
            LOG(LogLevel::ERROR) << "listen error";
            exit(LISTEN_ERR);
        }
        LOG(LogLevel::DEBUG) << "listen create success: " << _sockfd;
        return true;
    }
    virtual SockPtr Accepter(InetAddr *client) override // 目前我们先不实现接受连接的功能，因为这个函数要跟我们之后的实现有关
    {
        if (!client)
        {
            return nullptr;
        }

        struct sockaddr_in peer;
        socklen_t len = sizeof(peer);

        int newfd = ::accept(_sockfd, CONV(&peer), &len);

        if (newfd < 0)
        {
            LOG(LogLevel::WARN) << "accept error";
            return nullptr;
        }

        client->SetAddr(peer, len);
        return std::make_shared<TcpSocket>(newfd);
    }
    virtual void Close() override
    {
        if (_sockfd == gdefaultsockfd)
        {
            return;
        }

        ::close(_sockfd);
    }

    virtual int Recv(std::string *out)override
    {
        char buffer[4096];
        int n = ::recv(_sockfd, buffer, sizeof(buffer) - 1, 0);

        if (n > 0)
        {
            buffer[n] = 0;
            *out = buffer;
        }

        return n;
    }

    virtual int Send(std::string &in)override
    {
        int n = ::send(_sockfd, in.c_str(), in.size(), 0);
        return n;
    }

    virtual int Fd()override
    {
        return _sockfd;
    }


private:
    int _sockfd;
};

// int main()
// {
//     Socket *sk = new TcpSocket();
//     sk->BuildTcpSocketMethod(8080);
// }